<template>
  <div>
    <h5>ts基础语法知识,看代码</h5>
  </div>
</template>

<script lang="ts" setup>
//js中类型随意更改，不会报错，解释性语言,弱类型语言,只能在运行时发现错误。ts是预编译类型,强类型语言，可以提前发现错误。ts更多的是在传参时,或接口封装时，用ts类型限制
//js随意更改类型，或者重复，只能在运行后发现错误
//    var a='a'
//     a=2;
// var a='c'
// console.log(a);

//一.ts基本类型定义
//1.string类型
let str: string = "aa";
str = "33";
console.log(16, str);

//2.number 类型
let nums: number = 33;
console.log(nums);

//3.boolean 类型
let b2: boolean = true;
b2 = false;
console.log(b2);

// 4.Array数组类型:类型[]-在TypeScript开发中, 数组内最好存放的数据类型是固定的(string)
// 数组类型的定义有两种方式
// 数组类型的定义有两种方式
let names: string[] = ["a", "b"]; //推荐使用这种
let names2: Array<string> = ["c", "d"];

names.push("s"); //填加其它类型会报错
names2.push("s");
console.log(names, names2);

//5.tuple元组类型-多种元素的组合x:[string,number,等等]
// 5.1.先看下数组的弊端-数组中通常建议存放相同类型的元素，不同类型的元素是不推荐放在数组中。（可以放在对象或者元组中）
// let arr5:any[]=['aaa',2,3] //不推荐什么地方都用any,放不同类型
let arr5: string[] = ["aaa", "bb"];
console.log(arr5);

//5.2.放元组里-元组中特点每个元素都有自己特性的类型，根据索引值获取到的值可以确定对应的类型；
let arr6: [string, number] = ["aa", 22];
console.log("元组", arr6[0]);

//5.3.不同类型，也可放对象里
let obj = {
  a: 1,
  b: "2",
};
console.log(obj.a);

//6.Object类型-对象类型，也就是除number，string，boolean，symbol，null或undefined之外的类型
function create(obj: object) {
  console.log(obj);
}
create({ key: 1 }); //{key:1}
// create(null); // 会提醒报错
// create(11); // 会提醒报错

//7.undefined 和 null 是两个基本数据类型
let n1: null = null;
let n2: undefined = undefined;
console.log(n1, n2);

//8.枚举类型enum-这个世界有很多值是多个并且是固定的，比如：一年的季节：春、夏、秋、冬 ，有四个结果。这种变量的结果是固定的几个数据时，就是我们使用枚举类型的最好时机
enum Color {
  red,
  yellow,
  blue,
}
let col = Color.red;
console.log("枚举", col); //默认情况下，索引从0开始

//也可自定义编号
enum Color2 {
  yello = 1,
  pink,
  red,
}
let c = Color2.pink;
console.log(77, c); //2 索引值

//枚举类型提供的一个便利是你可以由枚举的值得到它的名字
enum Color3 {
  red,
  yellow,
  blue,
}
let d = Color3[0];
console.log(82, d); //red

//9.泛型是指在定义函数、接口或类的时候，不预先指定具体的类型，而在使用的时候再指定类型的一种特性
//表现形式：由尖括号包裹<T>, 其中 T 代表 Type，在定义泛型时通常用作第一个类型变量名称。但实际上 T 可以用任何有效名称代替。除了 T 之外，以下是常见泛型变量代表的意思： K（Key）：表示对象中的键类型； V（Value）：表示对象中的值类型； E（Element）：表示元素类型。

//如:以下在调用时返回string或number类型等不特定的数据-可以用泛型来代替
function getData1<T>(arg1: T): T {
  return arg1;
}
//在调用时传任何类型都可以
console.log("泛型", getData1(9));
console.log(getData1("9"));
console.log(getData1(true));

// 泛型也可以传入多个值
function swap<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]];
}

console.log("114", swap(["string", 123]));

//  --------------------------------------------------------

//二.数组元组
//1.1数组-ts里数组只能放定义那种类型数据
/*  let arr:number[]=[1,2]
    // arr.push('1') // 报错 类型“string”的参数不能赋给类型“number”的参数
    let arr1:string[]=['1','2']

    console.log('数组[]方式',arr,arr1);

    //1.2 使用泛型定义数组
    let arr2:Array<number>=[1,2]
    let str2:Array<string>=['a','b']
    console.log('数组泛型<>方式',arr2,str2);

    //2.元组-放不同类型
    let arr3:[number,string]=[1,'元组']
    arr3[0]=2  //可以只赋值其中一项
    // arr3=[2] //如果多个重新赋值的话，必须全部赋值，否则报错
    console.log(102,arr3);

    //3.类型推论
    // 什么是类型推论?TypeScript 会在没有明确的指定类型的时候推测出一个类型，这就是类型推论。

    let str3='类型推论'
   // str3=7  // 以下代码虽然没有指定类型，但是会在编译的时候报错
    console.log('类型推论',str3);
    // 3.2 推论any类型。如果定义的时候没有赋值，不管之后有没有赋值，都会被推断成 any 类型而完全不被类型检查：
    let str4;
    str4='a'
    str4=4 //输出最后一个
    console.log(str4);

    // 4.1 联合类型-联合类型表示取值可以为多种类型中的一种，使用 `|` 分隔每个类型
    let numStr:string|number;
    numStr='seven'
    numStr=2
    // numStr=true //以下是错误的,因为上面已经定义过类型，不能是其他类型
    console.log('联合类型',numStr);

    // 4.2 访问联合类型的属性或方法-当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候，我们**只能访问此联合类型的所有类型里共有的属性或方法
    function getNumStr(num2:string|number){
        // return num2.length  //报错
        return num2.toString()  //访问共有属性

    }
    console.log(129,getNumStr(22))  */

//  --------------------------------------------------------

/* // 三.接口-在ts中,接口起到一种限制和规范的作用
//1.1接口定义
 interface Iuser
    {
        name:string,
        age:number
    }
  //1.2接口使用-增加变量和减少变量是不允许的，改变属性的数据类型是不允许的；可见，赋值的时候，**变量的形状必须和接口的形状保持一致
  let tom:Iuser={
      name:'接口',
      age:15, //如果只给一个，会报错缺少属性。类型必须得跟定义的类型一致
      //sex:0, //类型与定义的保持一致，不能多，也不能少属性
      }

  //1.3 接口可选属性-有时接口里的属性可有可无，可定义为可选属性。类型的冒号前加上英文的问号(?)
  interface Iuser2
  {
      name:string,
      age:number,
      sex?:string, //定义可选属性
  //   [prop:string]:string|number  //  [propName: string] 定义任意属性。一旦定义了任意属性，那么确定属性和可选属性的类型都必须是它的类型的子集。意思是其它类型都必须跟该类型保持一致，否则会报错,如果有多个不同类型可用联合类型填加
  }
  let tom2:Iuser2={
      name:'接口',
      age:15,
  }

  //1.4 只读属性-有时候我们希望对象中的一些字段只能在创建的时候被赋值，后续不能在被更改。可用只读属性
  interface Iuser3
  {
      readonly id:number,
      name:string,
      age:number
  }
  let tom3:Iuser3={
      id:11,
      name:'接口',
      age:15,
  }
  // tom3.id=22 // 报错： 无法分配到 "id" ，因为它是只读属性
  tom.name='测试'
  console.log('接口只读属性',tom3);

  //1.5 可索引接口-对数组、对象的访问约束（不常用）
  // 数组定义
  interface Iarr {
      [prop:number]: string
  }
  // 使用接口约束数组
  let arr:Iarr = ['1','2']
  console.log(arr[1]);

  //对象定义
  interface Iobj {
      [prop:string]: string
  }

  let json:Iobj = {
      a: '1'
  }
  console.log(json['a']);  */

//2.接口应用-可用于约束,ajax传参请求
//2.1 先复习下ajax-注意有报错XMLHttpRequest
// let ajax = new XMLHttpRequest()
//   // 建立连接 open('请求方式','请求连接'，同步或者异步)
//   ajax.open('get','https://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata',true)
//   // 发送请求
//   ajax.send()
//   // ajax状态判断
//   ajax.onreadystatechange = function(){
//       if(ajax.status==200 && ajax.readyState==4){
//           console.log('接口数据',ajax.response);

//       }
//   }
//2.2 封装下
// function request(params){
//       let ajax = new XMLHttpRequest()
//       ajax.open(params.method,params.url)
//       ajax.send(params.data)
//       ajax.onreadystatechange = function(){
//           if(ajax.status==200 && ajax.readyState==4){
//               console.log(ajax.response);
//           }
//       }
//   }
//   request({method: 'get',url: 'https://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata'})

//2.3 使用ts定义接口约束-可对传入的参数进行强制限制
//  interface Iparams{
//       method:string,
//       url:string,
//       data?:any
//   }
//   function request(params:Iparams){
//       let ajax = new XMLHttpRequest() //注意此处会报错，XMLHttpRequest is not defined
//       ajax.open(params.method,params.url)
//       ajax.send(params.data)
//       ajax.onreadystatechange = function(){
//           if(ajax.status==200 && ajax.readyState==4){
//               console.log(ajax.response);
//           }
//       }
//   }
//   // request() //如果不传参数的话，编译时会报错
//   request({method: 'get',url: 'https://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata'})

//  --------------------------------------------------------

//四.断言
// 1.1类型断言as,字面理解就是可以用来手动指定一个值的类型——有时候ts无法获取具体的类型信息就要访问其中一个类型，这时我们就需要使用类型断言as或者断言另一种形式尖括号<类型>。推荐使用as
/*
//   function getLen(str:string|number){
//       return str.length;   //这时会报错-因为number类型没有length属性而报错
//   }
//   console.log(getLen('aa'))


  //解决办法
  function getLen1(str:string|number){
      return (str as string).length  //用断言as,
      // return (<string>str).length  //断言另一种形式尖括号<类型>
      // return (str as boolean).length  //注意：类型断言不是类型转换，断言成一个联合类型中不存在的类型是不允许的。会报错
  }
  console.log(getLen1('aa'))

  //1.2 使用断言注意问题-类型断言应该少用


  //2.函数约束
  //2.1 js中使用函数
  function addNum2(x,y){
      return x + y
  }
  console.log(addNum2(1,2))

  //2.2 ts中使用函数。TS中，一个函数要有输入（参数）和输出（返回值），我们在ts内要对其输入和输出进行约束
  // 对输入（参数）的约束是在每个参数后面写上 :类型
  // 对输出（返回值）的约束是在函数名()后写上 :类型
  function addNum(x:number,y:number):number{
      return x + y
  }
  // addNum() // 报错： 应有 2 个参数，但获得 0 个。
  // addNum(1) //报错： 应有 2 个参数，但获得 1 个。
  // addNum(1,2,3) //报错： 应有 2 个参数，但获得 3 个。
  addNum(1,2)


  //2.3 注意不要混淆了 TypeScript 中的 => 和 ES6 中的 =>  。在 TypeScript 的类型定义中，=> 用来表示函数的定义，左边是输入类型，需要用括号括起来，右边是输出类型。*
  // var addNum1:(x:number,y:number)=>number = function(x:number,y:number):number{
  //     return x + y
  // }

  // addNum1(1,2)

  //2.4 接口+表达式约束-采用函数表达式|接口定义函数的方式时，对等号左侧进行类型限制，可以保证以后对函数名赋值时保证参数个数、参数类型、返回值类型不变
  //定义接口
  //  interface Ifun {
  //       (x:number,y:number):number
  //   }
  //   // 接口约束函数
  //   var addNum4:Ifun = function(x:number,y:number):number{
  //       return x + y
  //   }
  //   addNum4(1,2)   */

//  --------------------------------------------------------
//五.函数
//1.函数定义: function 方法名() : 返回类型
/*   function run():string{
      return 'aa'
  }

  console.log(run())

  //2.ts函数传参
  function run1(user:string,age:number):any{
      return `${user},${age}`

  }

  console.log(run1('bb',11))

  //3.没有返回值的方法
  function run3():void{
    console.log('cc')
  }

  run3()

  //4.方法的可选参数-!!!注意可选参数必须配置到参数的最后面
  function run4(user:string,age?:number):string{
        if(age){
            return `${user},${age}`
        }
        else{
            return `${user}`
        }
  }

  console.log(run4('dd',11))
  console.log(run4('dd'))


  //5.默认参数-我们允许给函数的参数添加默认值，ts会将添加了默认值的参数识别为可选参数，此时就不受「可选参数必须接在必需参数后面」的限制了,放到哪里都可以
  function num(a:number,b:number=1):number{
      return a+b
  }
  console.log(num(2)) //3

  //6.剩余参数(三点运算)-假如传入的参数个数不确定时，可用剩余参数
  function num2(a:number,...args:number[]):number{
      let total=a
      args.forEach(item=>{
          total+=item
      })
      return total
  }
  console.log(num2(1,2)) //3
  console.log(num2(1,2,3)) //6


  //7.函数重载——指同名但是不同参数的函数方法，作用是可实现多种功能.函数重载允许一个函数接受不同数量或类型的参数时，作出不同的处理；也就是说通过为同一个函数提供多个函数类型定的义来实现多种功能。
  //函数重载，需要重复定义多次函数，前几次都是函数定义，最后一次是函数实现。在编辑器的代码提示中，可以正确的看到前两个提示.
  function run5(user:string):string;
  function run5(age:number):number;

  function run5(str:any):any{
      if(typeof str==='string'){
          return `我的名字是${str}`
      }
      else{
          return `我的年龄是${str}`
      }
  }

  console.log(run5('ee'))
  console.log(run5(18))  */

//  --------------------------------------------------------

//六.类的定义
/* //1.ts中定义类，与原生js差不多。——!!! 注意ts在声明类的时候，需要对类的属性的数据类型进行约束
  class Person{
    name1:string; //前面省略的public关键词
    constructor(name:string){
     this.name1=name
    }
    run():void{
      console.log(`${this.name1}在工作`);
    }

  }
  let p = new Person("张三");
  console.log(386,p.name1)
  p.run()


  //2.继承
  class person1{
    name:string;
    constructor(name:string) {
        this.name = name;
    }
    run():string{
        return `${this.name}在跑步`;
    }
  }

  let p1 = new person1("王吉");
  console.log(p1.run());

  // 继承
  class Web extends person1{
    constructor(name:string) {
        super(name);
    }
  }
  let w = new Web("李四");
  console.log(w.run());


  //3.ts提供了三种修饰符：
  // public(默认的)： 公有 在类里面、子类类外边都可以访问
  // protected：在类里面、子类里面可以访问、在类外部无法访问
  // private：在类里面可以访问、子类、类外部没法访问


  //4.静态属性 与静态方法区别
  //4.1 所谓静态方法-当类创建之初，该方法就存在。而对应的实例方法-只有实例化该类的时候，才存在
  // function Person2(){
  //   this.run1=function(){
  //     console.log(424,'我是实例方法');
  //   }
  // }
  // // 静态方法
  // Person2.run2=function(){
  //   console.log(429,'我是静态方法');

  // }

  // // 调用实例方法：(实例化之后才能调用的)
  // var p2 = new Person2();
  // p2.run1();

  // // 调用静态方法：
  // Person2.run2()


  //4.2 另一种方式声明静态方法，利用static关键字声明
  //!!!注意 静态方法里面没法直接调用类里面的属性
  class person3{
    name:string;
    // 静态方法
    static name1:string ="阿巴阿爸"
    constructor(name:string) {
        this.name = name;
    }
    //实例方法
    run():string{
        return `${this.name}在跑步`;
    }

    // 静态方法——里面没法直接调用类里面的属性
    static print(){
        return `我叫${this.name1}`;
    }
  }

  let p3 = new person3("小甜甜");
  console.log(462,p3.run());
  console.log(person3.print());



  //5.抽象方法
  //ts中的抽象类，它是提供其他类继承的基类，不能直接被实例化
  // 用abstract关键字定义抽象类和抽象方法，抽象方法只能放在抽象类中

  //抽象类-抽象类中必须包含至少一个抽象方法，不然没有意义。也就是说，抽象方法只能出现在抽象类中
  abstract class Animal1{
    name:string;
    constructor(name:string) {
        this.name =name;
    }
  //抽象方法
    abstract eat():any;
   // abstract eat2():string { return '' }; // 报错：方法“eat”不能具有实现，因为它标记为抽象 。也就是说抽象方法不能有具体体现，只能在派生类(即子类继承)中实现

  }

  // 抽象类无法直接实例化
  // var a = new Animal();// 会报错


   // 抽象类的子类必须实现抽象类里面的抽象方法
  class Dog1 extends Animal1{

    constructor(name:string) {
        super(name)
    }
    eat(){
        console.log(this.name+"快跑");

    }
  }

  var d = new Dog1("小狗")
  d.eat();// 小狗快跑

   */
</script>
